home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / lisp / elk-2_0.lha / elk-2.0 / contrib / regexp / gnu_regexp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-08  |  9.1 KB  |  310 lines

  1. /* $Header: /home/panda/pg/bevan/progs/elk/lib/RCS/gnu_regexp.c,v 1.5 91/04/02 14:32:25 bevan Exp $ */
  2.  
  3. /*+c
  4. ** DESCRIPTION
  5. **   Regular Expressions for ELK
  6. ** These are based on the GNU regular expression code
  7. ** The code is written in K&R C to maintain compatibility with the rest of ELK
  8. **
  9. ** USE:
  10. **   To avoid name clashes, everything is prefixed by gnu.  This will help
  11. ** if you add a different regexp package, such as Henry Spencer's.
  12. ** 
  13. ** gnu:regexp
  14. **   A type that represents gnu regular expressions.
  15. **
  16. ** (gnu:make-regexp str)
  17. **   Generate a regulare expression from the given string.  Does not return
  18. **   if there is an error in the string.
  19. **
  20. ** (gnu:regexp? obj)
  21. **   Is the object a GNU regular expression?
  22. **
  23. ** (gnu:regexp-exec regexp str start)
  24. **   Apply the GNU regular expression `regexp' to the string `str' starting
  25. **   at position `start'.  If the match succeeds it returns a gnu:regexp-match
  26. **   It returns #f otherwise.
  27. **
  28. ** gnu:regexp-match
  29. **   A type that represents regular expression matches.
  30. **
  31. ** (gnu:regexp-match? obj)
  32. **   Is the object a GNU regular expression match.
  33. **
  34. ** (gnu:regexp-start regexp-match match-number)
  35. **   Returns the start position of the match denoted by `match-number'
  36. **   The whole regexp is 0.  Each further number represents positions
  37. **   enclosed by \\(\\) sections.
  38. **
  39. ** (gnu:regexp-end regexp-match match-number)
  40. **   Returns the end position of the match denoted by `match-number'
  41. **   See the above.
  42. **
  43. **  Note the above produce values such that they can directly be used by
  44. ** substring 
  45. **
  46. ** The interface to the regexp code is a mixture of the Emacs style and that to
  47. ** the regexp package in Python.  This has been done so that there is a low
  48. ** level on which packages to emulate either of the above could be written.
  49. ** For example one nice feature of Python's regexp is that it returns a list
  50. ** of the match positions.  This can easily be emulated by consing up a list
  51. ** from a gnu:match
  52. **
  53. ** EXAMPLE
  54. **
  55. **  (define scheme-define-matcher
  56. **    (gnu:make-regexp ""^[ \t]*(define[ \t]+(?\\([---A-Za-z0-9:?]+\\)[ \t]*"))
  57. **
  58. **  This creates a regexp that will match most of the scheme function names
  59. **  I use. (Note it doesn't match symbolic ones like (define (* a b) ...))
  60. **
  61. **  (define str "(define (some-scheme-function a b) ...)")
  62. **
  63. **  (define match (gnu:regexp-exec scheme-define-matcher str 0))
  64. **
  65. **  (if (gnu:regexp-match? match)
  66. **      (let ((name (substring str
  67. **                             (gnu:regexp-start match 1)
  68. **                             (gnu:regexp-end match 1))))
  69. **        (display name))
  70. **      (error 'foo "couldn't find a match"))
  71. **
  72. **  should output
  73. **
  74. **    some-scheme-function
  75. **
  76. */
  77.  
  78.  
  79. #include <scheme.h>
  80.  
  81. /* Note that a .c file is being included here.  This is so that a single .o
  82. ** file is created rather than one for this file and one for the GNU regex
  83. ** code.  The reason for not wanting two .o files is that I can't think how to
  84. ** reliably make sure that the GNU regex .o is loaded in before this .o (One
  85. ** solution would be to add an init section with a Provide in it to it, but I'd
  86. ** rather not do that)
  87. */
  88. #include "gnu_regex.c"
  89.  
  90. /* Placeholders for the Gnu_Regexp and Gnu_RegexpMatch types. */
  91. static int T_Gnu_Regexp;
  92. static int T_Gnu_RegexpMatch;
  93.  
  94. /* A regular expression is represented as a pointer to 
  95. ** the compiled regular expression pattern.
  96. ** Is the nothing field really necessary?
  97. */
  98. struct S_Gnu_Regexp
  99. {
  100.   Object nothing;
  101.   struct re_pattern_buffer compiled_pattern;
  102.   Object the_pattern;
  103. };
  104.  
  105. /* A the result of a regular expression match is a structure
  106. ** storing the positions of the match.
  107. ** Is the nothing field really necessary ?
  108. */
  109. struct S_Gnu_RegexpMatch
  110. {
  111.   Object nothing;
  112.   struct re_registers matches;
  113. };
  114.  
  115. /* Convert from a generic ELK pointer to GNUREGEXPs and GNUREGEXPMATCHs. */
  116. #define GNUREGEXP(obj)    ((struct S_Gnu_Regexp *)POINTER(obj))
  117. #define GNUREGEXPMATCH(obj)    ((struct S_Gnu_RegexpMatch *)POINTER(obj))
  118.  
  119. /* The following set of functions are the standard ones you have to define
  120. ** inorder to create a type for ELK.
  121. */
  122.  
  123. /* Two Gnu_Regexps are eqv? if they share the strings that they are a pattern of */
  124.  
  125. static int Gnu_Regexp_Eqv(a, b)
  126.   Object a, b;
  127. {
  128.   return GNUREGEXP(a)->the_pattern == GNUREGEXP(b)->the_pattern;
  129. }
  130.  
  131. /* Two Gnu_Regexps are equal? if they represent the same pattern. */
  132.  
  133. static int Gnu_Regexp_Equal(a, b)
  134.   Object a, b;
  135. {
  136.   return General_Strcmp(GNUREGEXP(a)->the_pattern, GNUREGEXP(b)->the_pattern, 0) == 0;
  137. }
  138.  
  139. static void Gnu_Regexp_Print(regexp, port, raw, depth, len)
  140.   Object regexp, port;
  141.   int raw, depth, len;
  142. {
  143.   Printf(port, "#[gnu:regexp ");
  144.   Pr_String(port, GNUREGEXP(regexp)->the_pattern, 0);
  145.   Printf(port, "]");
  146. }
  147.  
  148. static void Gnu_Regexp_Visit(x, f)
  149.   Object *x;
  150.   void (*f)();
  151. {
  152.   struct S_Gnu_Regexp *p= GNUREGEXP(*x);
  153.   (*f)(&(p->the_pattern));
  154. }
  155.  
  156. static Object P_Gnu_Regexpp(x)
  157.   Object x;
  158. {
  159.   return TYPE(x) == T_Gnu_Regexp ? True : False;
  160. }
  161.  
  162. static int Gnu_RegexpMatch_Eqv(a, b)
  163.   Object a, b;
  164. {
  165.   return EQ(a, b);
  166. }
  167.  
  168. /* There is not good way to tell if two Gnu_RegexpMatches are equal rather than
  169. ** eqv, so just use the eqv definition.
  170. */
  171. static int Gnu_RegexpMatch_Equal(a, b)
  172.   Object a, b;
  173. {
  174.   return EQ(a, b);
  175. }
  176.  
  177. static void Gnu_RegexpMatch_Print(regexpm, port, raw, depth, len)
  178.   Object regexpm, port;
  179.   int raw, depth, len;
  180. {
  181.   Printf(port, "#[gnu:regexp-match %lu]", POINTER(regexpm));
  182. }
  183.  
  184. static Object P_Gnu_RegexpMatchp(x)
  185.   Object x;
  186. {
  187.   return TYPE(x) == T_Gnu_RegexpMatch ? True : False;
  188. }
  189.  
  190. /* End of standard functions */
  191.  
  192.  
  193. /* Given a string, it compiles it into a regular expression. */
  194. /* Is the Link + Unlink stuff necessary ? */
  195.  
  196. static Object P_Make_Gnu_Regexp(str)
  197.   Object str;
  198. {
  199.   Object regexp;
  200.   char *error;
  201.   GC_Node2;
  202.  
  203.   regexp= Null;
  204.   GC_Link2(str, regexp);
  205.   Check_Type(str, T_String);
  206.   regexp= Alloc_Object(sizeof(struct S_Gnu_Regexp), T_Gnu_Regexp, 0);
  207.   GNUREGEXP(regexp)->nothing= Null;
  208.   GNUREGEXP(regexp)->the_pattern= str;
  209.   /* the value 40 is a arbitrary initial buffer size */
  210.   GNUREGEXP(regexp)->compiled_pattern.allocated= 40;
  211.   GNUREGEXP(regexp)->compiled_pattern.buffer= Safe_Malloc(40);
  212.   GNUREGEXP(regexp)->compiled_pattern.fastmap= NULL;
  213.   GNUREGEXP(regexp)->compiled_pattern.translate= NULL;
  214.   error= re_compile_pattern(STRING(str)->data, STRING(str)->size, &GNUREGEXP(regexp)->compiled_pattern);
  215.   GC_Unlink;
  216.   if (error != (char *)0)
  217.     Primitive_Error(error);
  218.   return regexp;
  219. }
  220.  
  221. static Object P_Gnu_Regexp_exec(regexp, str, start)
  222.   Object regexp, str, start;
  223. {
  224.   int intStart;
  225.   int errorCode;
  226.   Object result;
  227.   GC_Node4;
  228.  
  229.   result= Null;
  230.   GC_Link4(regexp, str, start, result);
  231.   Check_Type(regexp, T_Gnu_Regexp);
  232.   Check_Type(str, T_String);
  233.   result= Alloc_Object(sizeof(struct S_Gnu_RegexpMatch), T_Gnu_RegexpMatch, 0);
  234.   GNUREGEXPMATCH(result)->nothing= Null;
  235.   intStart= Get_Integer(start);
  236.   errorCode= re_match(&GNUREGEXP(regexp)->compiled_pattern, STRING(str)->data,
  237.               STRING(str)->size, intStart, &GNUREGEXPMATCH(result)->matches);
  238.   GC_Unlink;
  239.   if (errorCode == -2)
  240.     Primitive_Error("Gnu_Regexp Stack Overflow");
  241.   return (errorCode == -1) ? False : result;
  242. }
  243.  
  244. /* Return the start position of a particular regular expression match. */
  245.  
  246. static Object P_Gnu_RegexpMatch_Start(regexp_match, match_number)
  247.   Object regexp_match, match_number;
  248. {
  249.   int int_match_number;
  250.   Check_Type(regexp_match, T_Gnu_RegexpMatch);
  251.   int_match_number= Get_Integer(match_number);
  252.   if (int_match_number >= RE_NREGS || int_match_number < 0)
  253.     Primitive_Error("Match number not in range ~s", match_number);
  254.   return Make_Integer(GNUREGEXPMATCH(regexp_match)->matches.start[int_match_number]);
  255. }
  256.  
  257. /* Return the end position of a particular regular expression match. */
  258.  
  259. static Object P_Gnu_RegexpMatch_End(regexp_match, match_number)
  260.   Object regexp_match, match_number;
  261. {
  262.   int int_match_number;
  263.   Check_Type(regexp_match, T_Gnu_RegexpMatch);
  264.   int_match_number= Get_Integer(match_number);
  265.   if (int_match_number >= RE_NREGS || int_match_number < 0)
  266.     Primitive_Error("Match number not in range ~s", match_number);
  267.   return Make_Integer(GNUREGEXPMATCH(regexp_match)->matches.end[int_match_number]);
  268. }
  269.  
  270.  
  271. /* Initialise the Gnu_Regexp extensions. */
  272.  
  273. void init_gnu_regexp()
  274. {
  275.   /* Define the Gnu_Regexp type */
  276.   T_Gnu_Regexp= Define_Type(
  277.     0,
  278.     "gnu:regexp",
  279.     NOFUNC,
  280.     sizeof(struct S_Gnu_Regexp),
  281.     Gnu_Regexp_Eqv,
  282.     Gnu_Regexp_Equal,
  283.     Gnu_Regexp_Print,
  284.     Gnu_Regexp_Visit
  285.   );
  286.   Define_Primitive(P_Make_Gnu_Regexp, "gnu:make-regexp", 1, 1, EVAL);
  287.   Define_Primitive(P_Gnu_Regexpp, "gnu:regexp?", 1, 1, EVAL);
  288.   Define_Primitive(P_Gnu_Regexp_exec, "gnu:regexp-exec", 3, 3, EVAL);
  289.  
  290.   /* Define the Gnu_RegexpMatch type
  291.   ** Notice that there is no public constructor for this type.
  292.   ** The only way a Gnu_RegexpMatch can be created is as the result
  293.   ** of a Gnu_Regexp-exec call
  294.   */
  295.   T_Gnu_RegexpMatch= Define_Type(
  296.     0,
  297.     "gnu:regexp-match",
  298.     NOFUNC,
  299.     sizeof(struct S_Gnu_RegexpMatch), 
  300.     Gnu_RegexpMatch_Eqv,
  301.     Gnu_RegexpMatch_Equal,
  302.     Gnu_RegexpMatch_Print,
  303.     NOFUNC
  304.   );
  305.   Define_Primitive(P_Gnu_RegexpMatchp, "gnu:regexp-match?", 1, 1, EVAL);
  306.   Define_Primitive(P_Gnu_RegexpMatch_Start, "gnu:regexp-start", 2, 2, EVAL);
  307.   Define_Primitive(P_Gnu_RegexpMatch_End, "gnu:regexp-end", 2, 2, EVAL);
  308.   P_Provide(Intern("gnu_regexp.o"));
  309. }
  310.